/**
 * 
 */
package br.com.ctm.advogadope.negocio.impl;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import javax.annotation.Resource;
import javax.annotation.security.PermitAll;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;
import javax.jms.Queue;
import javax.jms.Session;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceException;

import br.com.ctm.advogadope.dao.api.ProcessoDao;
import br.com.ctm.advogadope.model.Advogado;
import br.com.ctm.advogadope.model.Processo;
import br.com.ctm.advogadope.model.Tribunal;
import br.com.ctm.advogadope.model.Tribunal.TipoJuizo;
import br.com.ctm.advogadope.negocio.api.ProcessoNegocio;
import br.com.ctm.advogadope.negocio.api.ServicoPJe;
import br.com.ctm.advogadope.util.JAXBUtils;
import br.com.framework.negocio.exception.NegocioException;
import br.com.framework.negocio.impl.BaseNegocioImpl;
import br.com.framework.util.date.DateUtil;
import br.jus.cnj.intercomunicacao.beans.DataHora;
import br.jus.cnj.intercomunicacao.beans.NumeroUnico;
import br.jus.cnj.intercomunicacao.beans.ProcessoJudicial;
import br.jus.cnj.intercomunicacao.beans.RequisicaoConsultaProcesso;
import br.jus.cnj.intercomunicacao.beans.RespostaConsultaProcesso;
import br.jus.cnj.intercomunicacao.servico.Intercomunicacao;

/**
 * @author Cleber
 *
 */
@Stateless
@PermitAll
public class ProcessoNegocioImpl extends BaseNegocioImpl<Processo, ProcessoDao> implements
		ProcessoNegocio {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	public static final int INTERVALO_ATUALIZACAO = 60000 * 60;
	
	private static SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
	
	@Inject
	private ServicoPJe servicoPJe;
	
	@Resource(mappedName = "java:/ConnectionFactory")
    private ConnectionFactory conFactory;
 
    @Resource(mappedName = "java:/queue/advogadope-queue")
    private Queue queue;

	/**
	 * 
	 */
	public ProcessoNegocioImpl() {
	}
	
	@Inject
	protected void setDao(ProcessoDao dao) {
		super.setDao(dao);
	}
	
	@Override
	public Processo findByNPU(String npu) throws NegocioException {
		Processo p = new Processo();
		p.setNpu(npu);
		List<Processo> list = findByExample(p);
		if (!list.isEmpty()) {
			p = list.get(0);
		} else {
			throw new NoResultException("processo.erro.naoEncontrado");
		}
		return p;
	}
	
	/**
	 * @param npu
	 * @param movimentos
	 * @param dataReferencia
	 * @return
	 * @throws NegocioException 
	 */
	public ProcessoJudicial consultarProcessoJudicial(Processo processo,
			boolean movimentos, Date dataReferencia)
			throws NegocioException {
		return consultarProcessoJudicial(processo.getNpu(), processo.getTribunal().getId(), processo.getTipoJuizo(), movimentos, dataReferencia);
	}
	
	/**
	 * @param npu
	 * @param idTribunal
	 * @param tipoJuizo
	 * @param movimentos
	 * @param dataReferencia
	 * @return
	 * @throws NegocioException
	 */
	public ProcessoJudicial consultarProcessoJudicial(String npu, Long idTribunal, TipoJuizo tipoJuizo,
			boolean movimentos, Date dataReferencia)
			throws NegocioException {
		ProcessoJudicial processoJudicial = null;
		RequisicaoConsultaProcesso req = new RequisicaoConsultaProcesso();
		req.setIdConsultante("?");
		req.setSenhaConsultante("?");
		NumeroUnico numUnico = new NumeroUnico();
		numUnico.setValue(npu);
		req.setNumeroProcesso(numUnico);
		req.setMovimentos(movimentos);
		if (dataReferencia != null) {
			DataHora dataHora = new DataHora();
			dataHora.setValue(DateUtil.formatDate(dataReferencia,
					"yyyyMMddHHmmss"));
			req.setDataReferencia(dataHora);
		}
		RespostaConsultaProcesso res = getServico(idTribunal, tipoJuizo)
				.consultarProcesso(req);
		if (res.isSucesso() && res.getProcesso() != null) {
			processoJudicial = res.getProcesso();
		}
		return processoJudicial;
	}
	
	@Override
	public ProcessoJudicial buscarProcessoJudicial(Processo processo,
			boolean movimentos, Date dataReferencia) throws NegocioException {
		boolean atualizarProcesso = false;
		ProcessoJudicial processoJudicial = null;
		if (processo.getXmlProcessoJudicial() != null) {
			processoJudicial = JAXBUtils.unmarshal(
					processo.getXmlProcessoJudicial(), ProcessoJudicial.class);
			// se a data de atualização for mais antiga que o intervalo, tenta atualizar
			if ((System.currentTimeMillis() - processo.getDataAtualizacao().getTime()) > INTERVALO_ATUALIZACAO) {
				ProcessoJudicial processoJudicialAtualizado = null;
				try {
					processoJudicialAtualizado = consultarProcessoJudicial(processo, movimentos, dataReferencia);
					atualizarProcesso = true;
				} catch (NegocioException e) {
					LOGGER.warn(String.format("Nao foi possivel atualizar o processo %s. %s", processo.getNpu(), e.getMessage()), e);
				}
				if (atualizarProcesso) {
					try {
						Date dataMovimento1 = format.parse(processoJudicial.getMovimento().get(0).getDataHora().getValue());
						Date dataMovimento2 = format.parse(processoJudicialAtualizado.getMovimento().get(0).getDataHora().getValue());
						if (dataMovimento2.after(dataMovimento1)) {
							enviarNotificacao(processo);
						}
					} catch (ParseException e) {
						LOGGER.error("Falha na conversao de datas", e);
					} catch (Exception e) {
						LOGGER.error("Erro inesperado na conversao de datas", e);
					}
				}
			}
		} else {
			processoJudicial = consultarProcessoJudicial(processo, movimentos, dataReferencia);
			atualizarProcesso = true;
		}
		if (atualizarProcesso) {
			processo.setXmlProcessoJudicial(JAXBUtils.marshal(processoJudicial, ProcessoJudicial.class));
			processo.setDataAtualizacao(new Date());
			this.update(processo);
		}
		return processoJudicial;
	}

	/* (non-Javadoc)
	 * @see br.com.ctm.advogadope.negocio.api.ProcessoNegocio#buscarProcessoJudicial(java.lang.String, boolean, java.util.Date)
	 */
	public ProcessoJudicial buscarProcessoJudicial(Long processoId, boolean movimentos, Date dataReferencia) 
			throws NegocioException{
		Processo processo = findByID(processoId);
		return buscarProcessoJudicial(processo, movimentos, dataReferencia);
	}
	
	/**
	 * @param npu
	 */
	protected void enviarNotificacao(Processo processo) {
		Connection connection = null;
		Session session = null;
		try {
			LOGGER.debug("Enviando mensagem para a queue.");
			connection = conFactory.createConnection();
			session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
			final MessageProducer publisher = session.createProducer(queue);
			final ObjectMessage objMessage = session.createObjectMessage(processo);
			publisher.send(objMessage);
			LOGGER.debug("Mensagem enviada para queue.");
		} catch (JMSException e) {
			LOGGER.error("Falha ao publicar mensagem na queue.", e);
		} finally {
			try {
				if (null != session) {
					session.close();
				}
			} catch (JMSException exc) {
				LOGGER.warn(exc);
			}
			try {
				if (null != connection) {
					connection.close();
				}
			} catch (JMSException exc) {
				LOGGER.warn(exc);
			}
		}
	}
	
	@Override
	public void insert(Processo entidade) throws PersistenceException,
			NegocioException {
		Processo ex = new Processo();
		ex.setNpu(entidade.getNpu());
		ex.setTribunal(entidade.getTribunal());
		ex.setTipoJuizo(entidade.getTipoJuizo());
		List<Processo> list = findByExample(ex);
		if (!list.isEmpty()) {
			ex = list.get(0);
			entidade.setId(ex.getId());
		} else {
			super.insert(entidade);
		}
	}
	
	/**
	 * @param idTribunal
	 * @param tipoJuizo
	 * @return
	 * @throws NegocioException
	 */
	private Intercomunicacao getServico(Long idTribunal, TipoJuizo tipoJuizo) throws NegocioException{
		return servicoPJe.getServico(idTribunal, tipoJuizo);
	}

	@Override
	public List<Processo> findByAdvogado(Advogado advogado, int firstResult,
			int maxResults, boolean onlyActive) throws PersistenceException {
		return getDao().findByAdvogado(advogado, firstResult, maxResults, onlyActive);
	}

	@Override
	public List<Processo> findByFiltroEAdvogado(Processo filtro,
			Advogado advogado, int firstResult, int maxResults, boolean onlyActive)
			throws PersistenceException {
		return getDao().findByFiltroEAdvogado(filtro, advogado, firstResult, maxResults, onlyActive);
	}

	@Override
	public List<Processo> findByDtAtualizacaoMaiorQueIntervalo(int intervalo,
			int firstResult, int maxResults, boolean onlyActive)
			throws PersistenceException {
		return getDao().findByDtAtualizacaoMaiorQueIntervalo(intervalo, firstResult, maxResults, onlyActive);
	}

	@Override
	public Processo consultarProcesso(String npu, Long idTribunal,
			TipoJuizo tipoJuizo) throws NegocioException {
		Processo pEx = new Processo();
		pEx.setNpu(npu);
		pEx.setTipoJuizo(tipoJuizo);
		pEx.setTribunal(new Tribunal());
		pEx.getTribunal().setId(idTribunal);
		List<Processo> list = findByExample(pEx);
		Processo processo = null;
		if (!list.isEmpty()) {
			processo = list.get(0);
			ProcessoJudicial processoJudicial = JAXBUtils.unmarshal(
					processo.getXmlProcessoJudicial(), ProcessoJudicial.class);
			ProcessoJudicial processoJudicialNovo = null;
			boolean atualizarProcesso = false;
			if (processo.getXmlProcessoJudicial() != null && 
					(System.currentTimeMillis() - processo.getDataAtualizacao().getTime()) > INTERVALO_ATUALIZACAO) {
				try {
					processoJudicialNovo = consultarProcessoJudicial(processo, true, null);
				} catch (Exception e) { 
					LOGGER.error("Falha ao consultar processo: " + processo.getNpu(), e);
				}
				if (processoJudicialNovo != null) {
					processoJudicial = JAXBUtils.unmarshal(
							processo.getXmlProcessoJudicial(), ProcessoJudicial.class);
					try {
						Date dataMovimento1 = format.parse(processoJudicial.getMovimento().get(0).getDataHora().getValue());
						Date dataMovimento2 = format.parse(processoJudicialNovo.getMovimento().get(0).getDataHora().getValue());
						if (dataMovimento2.after(dataMovimento1)) {
							atualizarProcesso = true;
							enviarNotificacao(processo);
						}
					} catch (ParseException e) {
						LOGGER.error("Falha na conversao de datas", e);
					} catch (Exception e) {
						LOGGER.error("Erro inesperado na conversao de datas", e);
					}
				}
			}
			
			if (atualizarProcesso) {
				processo.setXmlProcessoJudicial(JAXBUtils.marshal(processoJudicialNovo, ProcessoJudicial.class));
				processo.setDataAtualizacao(new Date());
				this.update(processo);
			}
			
		} else {
			ProcessoJudicial processoJudicialNovo = null;
			try {
				processoJudicialNovo = consultarProcessoJudicial(npu, idTribunal, tipoJuizo, true, null);
			} catch (NegocioException e) {
				LOGGER.error("Nao foi possivel consultar o processo: " + npu, e);
			}
			if (processoJudicialNovo != null) {
				processo = new Processo();
				processo.setNpu(npu);
				processo.setTribunal(new Tribunal());
				processo.getTribunal().setId(idTribunal);
				processo.setTipoJuizo(tipoJuizo);
				processo.setXmlProcessoJudicial(JAXBUtils.marshal(processoJudicialNovo, ProcessoJudicial.class));
				processo.setDataAtualizacao(new Date());
				this.insert(processo);
			}
		}
		return processo;
	}

}
